package com.ctakit.sdk.app.widget.jsbridge;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

import com.ctakit.sdk.SdkApplication;
import com.ctakit.sdk.app.BaseApplication;
import com.ctakit.sdk.app.base.BaseActivity;
import com.ctakit.sdk.app.service.ActionCallBack;
import com.ctakit.sdk.app.service.SimpleActionCallBack;
import com.ctakit.sdk.app.util.permissionsutil.PermissionsChecker;
import com.ctakit.sdk.http.okhttp.OkHttpUtils;
import com.ctakit.sdk.http.okhttp.callback.FileCallBack;
import com.ctakit.sdk.util.FileUtils;
import com.ctakit.sdk.util.JsonUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import okhttp3.Call;

/**
 * Created by sll on 2016/5/5.
 */
public class WVJBWebView extends ProgressWebView {

    private ArrayList<WVJBMessage> messageQueue = new ArrayList<>();
    private Map<String, WVJBResponseCallback> responseCallbacks = new HashMap<>();
    private Map<String, WVJBHandler> messageHandlers = new HashMap<>();
    private long uniqueId = 0;
    private MyJavascriptInterface myInterface = new MyJavascriptInterface();
    private String script;
    private H5CallBack h5CallBack;
    private Context myContext;

    protected ActionCallBack<String> actionTitleCallBack;
    private SimpleActionCallBack<String> pageFinishedActionCallBack;


    public interface WVJBResponseCallback {
        void callback(Object data);
    }

    public interface WVJBHandler {
        void request(Object data, WVJBResponseCallback callback);
    }


    public WVJBWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        myContext = context;
        init();
    }


    protected String getUserAgentWebView() {
        return null;
    }

    private void init() {
        this.getSettings().setJavaScriptEnabled(true);
        String agent = getUserAgentWebView();
        if (!TextUtils.isEmpty(agent)) {
            this.getSettings().setUserAgentString(agent);
        }

        this.setVerticalScrollBarEnabled(false);
        this.setHorizontalScrollBarEnabled(false);

        this.getSettings().setDomStorageEnabled(true);
        this.getSettings().setDatabaseEnabled(true);
        this.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        this.getSettings().setSupportMultipleWindows(true);
        this.clearCache(true);
        this.destroyDrawingCache();
        this.getSettings().setAppCacheEnabled(false);
        if (Build.VERSION.SDK_INT >= 21) {
            this.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

            this.getSettings().setAllowUniversalAccessFromFileURLs(true);
            this.getSettings().setAllowFileAccessFromFileURLs(true);
            this.getSettings().setAllowContentAccess(true);
            this.getSettings().setAppCacheEnabled(true);

        }


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebView.setWebContentsDebuggingEnabled(true);
        }


        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        //cookieManager.setCookie(url, cookies);//cookies是在HttpClient中获得的cookie
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            CookieSyncManager.getInstance().sync();
        }
        addJavascriptInterface(myInterface, WVJBConstants.INTERFACE);
        setWebViewClient(new MyWVJBWebViewClient(this));


        //
        // this.setDefaultHandler(new DefaultHandler());
        initDefalultRegisterHandler();
        initRegisterHandler();
    }

    public void initRegisterHandler() {

    }

    private void initDefalultRegisterHandler() {


        registerHandler("callApp", new WVJBHandler() {
            @Override
            public void request(Object data, WVJBResponseCallback callback) {
                try {
                    if (h5CallBack != null) {
                        H5CallAction h5CallAction = JsonUtils.fromJson(data + "", H5CallAction.class);
                        if (h5CallAction != null && h5CallAction.getType() != null) {
                            h5CallBack.callApp(h5CallAction, callback);
                        }
                    }

                } catch (Exception e) {

                }

            }
        });
    }

    @Override
    protected boolean overrideUrlLoading(WebView view, String url) {
        String decodeUrl = url;
        try {
            decodeUrl = URLDecoder.decode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        if (onWebLoadListener != null) {
            if (onWebLoadListener.onWebStartLoad(decodeUrl, url)) {
                return true;
            }
        }

        return false;
    }

    public void registerH5CallBack(H5CallBack h5CallBack) {
        this.h5CallBack = h5CallBack;
    }

    public void callHandler(String handlerName) {
        callHandler(handlerName, null, null);
    }

    public void callHandler(String handlerName, Object data) {
        callHandler(handlerName, data, null);
    }

    public void callHandler(String handlerName, Object data,
                            WVJBResponseCallback callback) {
        sendData(data, callback, handlerName);
    }

    public void registerHandler(String handlerName, WVJBHandler handler) {
        if (TextUtils.isEmpty(handlerName) || handler == null) {
            return;
        }

        messageHandlers.put(handlerName, handler);
    }


    private void sendData(Object data, WVJBResponseCallback callback,
                          String handlerName) {
        if (data == null && TextUtils.isEmpty(handlerName))
            return;
        WVJBMessage message = new WVJBMessage();
        if (data != null) {
            message.data = data;
        }
        if (callback != null) {
            String callbackId = "java_cb_" + (++uniqueId);
            responseCallbacks.put(callbackId, callback);
            message.callbackId = callbackId;
        }
        if (handlerName != null) {
            message.handlerName = handlerName;
        }
        queueMessage(message);
    }


    private void queueMessage(WVJBMessage message) {
        if (messageQueue != null) {
            messageQueue.add(message);
        } else {
            dispatchMessage(message);
        }
    }

    public void dispatchMessage(WVJBMessage message) {
        String messageJSON = doubleEscapeString(message2Json(message).toString());
        executeJavascript("WebViewJavascriptBridge._handleMessageFromJava('"
                + messageJSON + "');");
    }


    private String doubleEscapeString(String javascript) {
        String result;
        result = javascript.replace("\\", "\\\\");
        result = result.replace("\"", "\\\"");
        result = result.replace("\'", "\\\'");
        result = result.replace("\n", "\\n");
        result = result.replace("\r", "\\r");
        result = result.replace("\f", "\\f");
        return result;
    }

    private JSONObject message2Json(WVJBMessage message) {
        JSONObject object = new JSONObject();
        try {
            if (message.callbackId != null) {
                object.put("callbackId", message.callbackId);
            }
            if (message.data != null) {
                object.put("data", message.data);
            }
            if (message.handlerName != null) {
                object.put("handlerName", message.handlerName);
            }
            if (message.responseId != null) {
                object.put("responseId", message.responseId);
            }
            if (message.responseData != null) {
                object.put("responseData", message.responseData);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return object;
    }


    public void flushMessageQueue() {
        String script = "WebViewJavascriptBridge._fetchQueue()";
        executeJavascript(script, new JavascriptCallback() {
            public void onReceiveValue(String messageQueueString) {
                if (!TextUtils.isEmpty(messageQueueString)) {
                    processMessageQueue(messageQueueString);
                }
            }
        });
    }

    private void processMessageQueue(String messageQueueString) {
        if (TextUtils.isEmpty(messageQueueString)) {
            return;
        }
        try {
            JSONArray messages = new JSONArray(messageQueueString);
            for (int i = 0; i < messages.length(); i++) {
                JSONObject jo = messages.getJSONObject(i);
                final WVJBMessage message = json2Message(jo);
                if (message.responseId != null) {
                    final WVJBResponseCallback responseCallback = responseCallbacks
                            .remove(message.responseId);
                    if (responseCallback != null) {
                        responseCallback.callback(message.responseData);
                    }
                } else {
                    WVJBResponseCallback responseCallback = null;
                    if (message.callbackId != null) {
                        final String callbackId = message.callbackId;
                        responseCallback = new WVJBResponseCallback() {
                            @Override
                            public void callback(Object data) {
                                WVJBMessage msg = new WVJBMessage();
                                msg.responseId = callbackId;
                                msg.responseData = data;
                                queueMessage(msg);
                            }
                        };
                    }

                    final WVJBHandler handler = messageHandlers.get(message.handlerName);
                    final WVJBResponseCallback finalresponseCallback = responseCallback;

                    if (handler != null) {
                        this.post(new Runnable() {
                            @Override
                            public void run() {
                                handler.request(message.data, finalresponseCallback);
                            }
                        });

                    } else {
                        Log.e(WVJBConstants.TAG, "No handler for message from JS:" + message.handlerName);
                    }
                }
            }
        } catch (JSONException exception) {
            exception.printStackTrace();
        }

    }

    private WVJBMessage json2Message(JSONObject object) {
        WVJBMessage message = new WVJBMessage();
        try {
            if (object.has("callbackId")) {
                message.callbackId = object.getString("callbackId");
            }
            if (object.has("data")) {
                message.data = object.get("data");
            }
            if (object.has("handlerName")) {
                message.handlerName = object.getString("handlerName");
            }
            if (object.has("responseId")) {
                message.responseId = object.getString("responseId");
            }
            if (object.has("responseData")) {
                message.responseData = object.get("responseData");
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return message;
    }


    public void injectJavascriptFile() {
        try {
            if (TextUtils.isEmpty(script)) {
                InputStream in = getResources().getAssets().open("WebViewJavascriptBridge.js");
                script = convertStreamToString(in);
            }
            executeJavascript(script);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (messageQueue != null) {
            for (WVJBMessage message : messageQueue) {
                dispatchMessage(message);
            }
            messageQueue = null;
        }
    }


    private String convertStreamToString(InputStream is) {
        String s = "";
        try {
            Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
            if (scanner.hasNext()) s = scanner.next();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return s;
    }

    private void executeJavascript(String script) {
        executeJavascript(script, null);
    }

    private void executeJavascript(final String script,
                                   final JavascriptCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            evaluateJavascript(script, new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    if (callback != null) {
                        if (value != null && value.startsWith("\"")
                                && value.endsWith("\"")) {
                            value = value.substring(1, value.length() - 1)
                                    .replaceAll("\\\\", "");
                        }
                        try {
                            callback.onReceiveValue(URLDecoder.decode(value, "utf-8"));
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        } else {
            if (callback != null) {
                myInterface.addCallback(++uniqueId + "", callback);
                post(new Runnable() {
                    @Override
                    public void run() {
                        loadUrl("javascript:window." + WVJBConstants.INTERFACE
                                + ".onResultForScript(" + uniqueId + "," + script + ")");
                    }
                });
            } else {
                post(new Runnable() {
                    @Override
                    public void run() {
                        loadUrl("javascript:" + script);
                    }
                });
            }
        }
    }


    private class MyJavascriptInterface {
        Map<String, JavascriptCallback> map = new HashMap<>();

        public void addCallback(String key, JavascriptCallback callback) {
            map.put(key, callback);
        }

        @JavascriptInterface
        public void onResultForScript(String key, String value) {
            JavascriptCallback callback = map.remove(key);
            if (callback != null)
                try {
                    callback.onReceiveValue(URLDecoder.decode(value, "utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
        }
    }

    public interface JavascriptCallback {
        void onReceiveValue(String value);
    }


    public interface H5CallBack {
        void callApp(H5CallAction h5CallAction, WVJBResponseCallback function);

        void setSharContent(ShareContent sharContent);
    }

    class MyWVJBWebViewClient extends WVJBWebViewClient {

        public MyWVJBWebViewClient(WVJBWebView wvjbWebView) {
            super(wvjbWebView);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            MyMimeType myMimeType = getOpenMimType(url);
            if (myMimeType != null) {
                if (!PermissionsChecker.checkPermission(myContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    if (myContext instanceof BaseActivity) {
                        ((BaseActivity) myContext).askPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, false);

                        // Toast.makeText(SdkApplication.app(), "请开启SD卡相关权限", Toast.LENGTH_SHORT).show();
                        //  PermissionsChecker.showMissingPermissionDialog((BaseActivity)myContext," 访问SD卡 ");
                    }
                    return true;
                } else {
                    displayFromFile(url, FileUtils.getFileNameWithExtension(url), myMimeType);
                }
                displayFromFile(url, FileUtils.getFileNameWithExtension(url), myMimeType);
                return true;
            }
            if (super.shouldOverrideUrlLoading(view, url)) {
                return true;
            }
            if (overrideUrlLoading(view, url)) {
                return true;
            }

            if (!url.startsWith("http:") && !url.startsWith("https:")) {
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    SdkApplication.app().startActivity(intent);
                } catch (Exception e) {
                    Toast.makeText(SdkApplication.app(), "无法打开链接", Toast.LENGTH_SHORT).show();
                }
                return true;
            } else {
                view.loadUrl(url, ((BaseApplication) SdkApplication.app()).getGlobalHeaders(true));
                return true;
            }

        }


        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();
          //  super.onReceivedSslError(view, handler, error);
        }


        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            if (titleMap.get(url) != null) {
                if (actionTitleCallBack != null) {
                    actionTitleCallBack.onSuccess(titleMap.get(url));
                }
            }
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (view == null) {//网络差的时候可能为空
                return;
            }
            injectJavascriptFile();
            if (pageFinishedActionCallBack != null) {
                pageFinishedActionCallBack.onSuccess(url);
            }
        }

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            return super.shouldInterceptRequest(view, request);
        }

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
            return super.shouldInterceptRequest(view, url);
        }
    }

    public void setPageFinishedActionCallBack(SimpleActionCallBack<String> pageFinishedActionCallBack) {
        this.pageFinishedActionCallBack = pageFinishedActionCallBack;
    }


    protected void openFile(File result, String fileType, String mimeType) {
        Intent target = new Intent(Intent.ACTION_VIEW);
        target.setDataAndType(Uri.fromFile(result), mimeType);
        target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
        Intent intent = Intent.createChooser(target, "Open File");
        intent.setFlags(target.getFlags());
        try {
            getContext().startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(SdkApplication.app(), String.format("你的手机当前不支持打开%s格式,请安装相应APP后查看", fileType), Toast.LENGTH_SHORT).show();
        }
    }


    /**
     * 获取打开网络的pdf文件
     *
     * @param fileUrl
     * @param fileName
     */
    private void displayFromFile(String fileUrl, String fileName, final MyMimeType myMimeType) {
        // pdfView.fromUri(this,this,this,fileUrl,fileName);   //设置pdf文件地址
        if (!FileUtils.hasSdcard()) {
            Toast.makeText(SdkApplication.app(), "你的手机当前没有SD卡,请插入", Toast.LENGTH_SHORT).show();
            return;
        }
        String sdpath = Environment.getExternalStorageDirectory().getPath() ;
        String mSavePath = sdpath + "/download";

        File myFile = new File(mSavePath + "/" + fileName);
        if (myFile.exists() && myFile.isFile()) {
            openFile(myFile, myMimeType.ext, myMimeType.type);
        } else {
            progressbar.setVisibility(VISIBLE);
            // String path = SdkApplication.app().getCacheDir().getPath() + "/";

            OkHttpUtils//
                    .get()//
                    .url(fileUrl)//
                    .build()//
                    .execute(new FileCallBack(mSavePath, fileName)//
                    {
                        @Override
                        public void onError(Call call, Exception e, int id) {
                            progressbar.setVisibility(GONE);
                        }

                        @Override
                        public void onResponse(File pdfFile, int id) {
                            //pdfFile.getAbsolutePath();
                            openFile(pdfFile, myMimeType.ext, myMimeType.type);
                        }

                        @Override
                        public void inProgress(float newProgress, long total, int id) {
                            int progress = (int) (newProgress * 100);
                            if (progress == 100) {
                                progressbar.setVisibility(GONE);
                            } else {
                                if (progressbar.getVisibility() == GONE) {
                                    progressbar.setVisibility(VISIBLE);
                                }
                                progressbar.setProgress(progress);
                            }
                        }

                    });
        }
    }


    protected MyMimeType getOpenMimType(String fileName) {
        return null;
    }

}